<?php

/*****************************************************************
Copyright (c) 2008-2009 {kissmvc.php version 0.7}
Eric Koh <erickoh75@gmail.com> http://kissmvc.com
Suwandi Tan <support@freelancecode.cz.cc> http://freelancecode.cz.cc

Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
*****************************************************************/
//===============================================================
// Controller
// Parses the HTTP request and routes to the appropriate function
//===============================================================

abstract class MV_Controller
{
	protected $starttime = '';
	protected $controller_path = ''; 
	protected $default_controller;
	protected $method = '';
	protected $request_method = '';
	
	protected $active_controller;
	protected $params = array();
	
	function __construct()
	{
		$this->controller_path = DOCROOT.'controllers';
		
		$this->default_controller = isset($this->params['controller'])? $this->params['controller'] : $GLOBALS['default_controller'];
		$this->method = isset($this->params['method'])? $this->params['method'] : 'index';
		
		$this->active_controller = $this->default_controller;
		
		$_GET = $this::sanitize($_GET);
		$_POST = $this::sanitize($_POST);
		$_COOKIE = $this::sanitize($_COOKIE);
		
	}
	
	public static function elapsed()
	{
		return round((microtime('time') - $GLOBALS['starttime']),4);
	}
	
	public function start()
	{
		$this->_get_request();
		$this->_parse_request();
		$this->_route_request();
	}
	
	private function _detect_http_method()
	{
		// Check method from inputted server
		$method = strtolower($_SERVER['REQUEST_METHOD']);
		
		// Finally, check if there is in HTTP method array
		if (in_array($method, array('get','post','put','delete')))
		{
			return $method; 
		}
		
		// if any method not found, just using get
		return 'get';
	}
	
	public static function get()
	{
	}
	
	public static function post()
	{
	}
	
	public static function put()
	{
	}
	
	public static function delete()
	{
	}
	
	private function _get_request()
	{
		$uri = substr($_SERVER['REQUEST_URI'], strlen(WEBFOLDER));
		$this->request_method = $this->_detect_http_method();
		
		return $uri;
	}
	
	private function _parse_request()
	{
		$uri = $this->_get_request();
		$this->request = explode('/', $uri);
		
		$is_controller = TRUE;
		$is_method = FALSE;
		
		foreach($this->request as $key => $val)
		{
			
			if(is_dir($this->controller_path.DS.$val))
			{
				$this->controller_path = $this->controller_path.DS.$val;
			}
			else
			{
				$check = str_replace('//','/',sprintf('%s%s%s', $this->controller_path.DS, $val, EXT));
				
				if (is_file($check)) 
				{
					$this->active_controller = $val;
					$is_controller = FALSE;
					$is_method = TRUE;
				}
				elseif( ! is_file($check) AND $is_method)
				{
					$this->method = $val;
					$is_method = FALSE;
				}
				else
				{
					$this->params[]= $val;
				}
			
			}
		}	
		
		if($is_controller AND count($this->request) > 1)
		{
			$this->active_controller = '';
		}
	}
	
	public function load($type, $file, $params = array())
	{
		switch($type)
		{
			case 'controllers':
				$filename = str_replace(DS.DS,DS,sprintf('%s%s%s', $this->controller_path.DS, $this->active_controller, EXT));
				break;
				
			case 'model':
				$filename = sprintf('%smodel'.DS.'%s%s',APPPATH, $file, EXT);
				break;
				
			case 'libraries':
				$filename = sprintf('%slibraries'.DS.'%s%s',APPPATH, $file, EXT);
				break;
			
			default:
				$filename = $file.EXT;
				break;
		}
		
		if ( ! is_file($filename)) 
		{
			$this->request_not_found($file.' file not found: ');
		}
		
		require_once $filename;
		 
		$class = ucfirst($file);
		return new $class($params);
		
	}
	
	private function _route_request()
	{
		$this->active_controller = $this->load('controllers', $this->active_controller);
		
		if ( ! method_exists($this->active_controller, $this->method)) {
			$this->request_not_found('Invalid method name: ' . $this->method);
		}
		
		switch($this->request_method)
		{
			case 'get':
				$this->get();
				break;
			
			case 'post':
				$this->post();
				break;
			
			case 'put':
				$this->put();
				break;
			
			case 'delete':
				$this->delete();
				break;
			
			default:
				$this->request_not_found('HTTP method not supported: '.$request_method);
				break;
		}
		
		call_user_func_array(array($this->active_controller, $this->method), $this->params);
	}
	
	public static function sanitize($value)
	{
		$bad = array(
						"../",
						"<!--",
						"-->",
						"<",
						">",
						"'",
						'"',
						'&',
						'$',
						'#',
						'{',
						'}',
						'[',
						']',
						'=',
						';',
						'?',
						"%20",
						"%22",
						"%3c",		// <
						"%253c",	// <
						"%3e",		// >
						"%0e",		// >
						"%28",		// (
						"%29",		// )
						"%2528",	// (
						"%26",		// &
						"%24",		// $
						"%3f",		// ?
						"%3b",		// ;
						"%3d"		// =
					);

		
		$bad[] = './';
		$bad[] = '/';
		//return stripslashes(str_replace($bad, '', $str));
		if (is_array($value) OR is_object($value))
		{
			foreach ($value as $key => $val)
			{
				// Recursively clean each value
				$value[$key] = Controller::sanitize($val);
			}
		}
		elseif (is_string($value))
		{
			if ((bool)get_magic_quotes_gpc() === TRUE)
			{
				// Remove slashes added by magic quotes
				$value = stripslashes($value);
			}

			if (strpos($value, "\r") !== FALSE)
			{
				// Standardize newlines
				$value = str_replace(array("\r\n", "\r"), "\n", $value);
			}
		}

		return $value;
	}
	
	
	//Override this function for your own custom 404 page
	function request_not_found($msg='') {
		header("HTTP/1.0 404 Not Found");
		die('<html><head><title>404 Not Found</title></head><body><h1>Not Found</h1><p>'.$msg.'<p>The requested URL was not found on this server.</p><p>Please go <a href="javascript: history.back(1)">back</a> and try again.</p><hr /><p>Powered By: <a href="http://freelancecode.cz.cc">Chilik</a></p></body></html>');
	}
}


abstract class Template
{
	protected $tpl = NULL;
	protected $name = '';
	protected $var = array(
		'tag_comment'  => array('{#', '#}'),
    		'tag_block'    => array('{%', '%}'),
	    	'tag_variable' => array('{{', '}}'),
	);
	
	function __construct()
	{
		// smarty for first default template
		if($GLOBALS['smarty_template'])
		{
			$this->name = 'smarty';
			
			require_once APPPATH."libraries/smarty/smarty.class.php";
			$this->tpl = new Smarty();

			$this->tpl->compile_check = TRUE;
			$this->tpl->force_compile = TRUE;
		
			$this->tpl->debugging = FALSE;
			$this->tpl->caching = TRUE;
			$this->tpl->cache_lifetime = 120;
			$this->tpl->left_delimiter = "{{";
			$this->tpl->right_delimiter = "}}";
		
			$this->tpl->template_dir = APPPATH."views/templates/";
			$this->tpl->compile_dir = APPPATH."views/templates_c/";
			$this->tpl->config_dir = APPPATH."libraries/smarty/configs/";
		
			$this->tpl->assign('APPPATH', APPPATH);
			$this->tpl->assign('SYSPATH', SYSPATH);
		}
		elseif($GLOBALS['twig_template'])
		{
			$this->name = 'twig';
			
			include APPPATH."libraries/Twig/Autoloader".EXT;
			
			Twig_Autoloader::register();
			
			$template_dir = new Twig_Loader_Filesystem(APPPATH."views/templates/twig");
			
			$this->tpl = new Twig_Environment($template_dir, array(
							'cache' => APPPATH."views/templates_c",
							'auto_reload' => TRUE,
							'APPPATH' => APPPATH,
							'SYSPATH' => SYSPATH,
		
						));
			
			$lexer = new Twig_Lexer($this->tpl, $this->var);
			
			$this->tpl->setLexer($lexer);
			
		}
		else
		{
			$this->name = 'chilik'; // json format
			return $this;
		}
	}
	
	public function set_delimiters($left = '{{', $right = '}}')
	{
		$this->tpl->left_delimiter($left);
		$this->tpl->right_delimiter($right); 
	}
	
	public function assign($key, $value = null, $nocache = false)
	{
		$this->tpl->assign($key, $value);
	}
	
	public function display($template, $cache_id = null, $compile_id = null, $parent = null)
	{
		$this->tpl->display($template);
	}
	
	public function fetch($template, $cache_id = null, $compile_id = null, $parent = null, $display = false)
	{
		$this->tpl->fetch($this->tpl->template_dir.$template);
	}
	
	function render($doc = "index", $array = array())
	{
		switch($this->name)
		{
			case 'smarty':
				foreach($array as $key => $val)
				{
					$this->tpl->assign($key, $val);
				}
		
				$this->tpl->display($this->tpl->template_dir.$doc.".tpl");
				
				break;
				
			case 'twig':
				$template = $this->tpl->loadTemplate($doc.'.tpl');
				echo $template->render($array);
				break;
				
			case 'chilik':
			default:
				$this->response($array, 200);
				break;
		}
		
	}
	
	public function response($data = array(), $http_code = NULL, $output='')
	{
		if(empty($data) && $http_code === NULL)
		{
			$http_code = '404';
		}
		else
		{
			is_numeric($http_code) OR $http_code = '200';
		}
		
		$output =  json_encode($data);
		
		header('HTPP/1.1: ' . $http_code);
		header('Content-Type: '.'application/json');
		header('Status: ' . $http_code);
		header('Content-Length: ' . strlen($output));
		
		exit($output);
		
	}
}



abstract class MV_Model  {
	
	private static $_instance = NULL;
	
	public $dsn = '';
	public $user = '';
	public $pass = '';
	
	protected $pdo = NULL;
	protected $lastresult;
	protected $pkname;
	
	protected $tablename;
	
	protected $QUOTE_STYLE='MYSQL'; // valid types are MYSQL,MSSQL,ANSI
	protected $COMPRESS_ARRAY=true;
	
	public $rs = array(); // for holding all object property variables
	
	private $is_connected = FALSE;
	
	function __construct() 
	{
	}
	/*
	public function setup($dsn, $user = NULL, $pass = NULL )
	{
		$this->dsn = $dsn;
		$this->user = $user;
		$this->pass = $pass;
		
		return $this;
	}
	
	function db_connect()
	{
		if($this->is_connected)
		{ 
			return;
		}
		
		try
		{
			$this->pdo = new PDO($this->dsn, $this->user, $this->pass);
			$this->pdo->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION ); 
		}
		catch(PDOException $e)
		{
			file_put_contents('PDOErrors.txt', 
				time() .': '. $e->getMessage(), FILE_APPEND);
		}
		
		$this->is_connected = TRUE;
		
		return $this->pdo;
	}
	*/
	
	private function _get_connected()
	{
		try
		{
			$driver = $GLOBALS['default_database']['driver'];
			$db = $GLOBALS['default_database']['database'];
			$user = isset($GLOBALS['default_database']['pass']) ? $GLOBALS['default_database']['pass']: '';
			$pass = isset($GLOBALS['default_database']['pass']) ? $GLOBALS['default_database']['pass']: '';
			
			$this->pdo = new PDO($driver. ":" . $db, $user, $pass);
			$this->pdo->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION ); 
		}
		catch(PDOException $e)
		{
			log_message('error', $e->getMessage());
			
			file_put_contents('PDOErrors.txt', 
				time() .': '. $e->getMessage(), FILE_APPEND);
		}
		
		return $this->pdo;
		
	}
	
	public function db_connect()
	{
		return $this->_get_connected();
	}
	
	public function db_close()
	{
		return $this->_get_disconnected();
	}
	
	private function _get_disconnected()
	{
		$this->pdo = NULL;
		$this->is_connected = FALSE;
	}
	
	protected function enquote($name) {
		if ($this->QUOTE_STYLE=='MYSQL')
			return '`'.$name.'`';
		elseif ($this->QUOTE_STYLE=='MSSQL')
			return '['.$name.']';
		else
			return '"'.$name.'"';
	}
	
	public function begin()
	{
		$this->query("BEGIN");
	}

	public function commit()
	{
		$this->query("COMMIT");
	}

	public function rollback()
	{
		$this->query("ROLLBACK");
	}

	//generic query wrapper
	public function query($sql)
	{
		$conn = $this->mydb;
		$result = $conn->query($sql);
		$this->lastresult = $result;
		return $result;
	}
	
	function select_single($sql, $params = array(), $pdo_fetch_mode=PDO::FETCH_OBJ)
	{
		/*
		$row = $this->select($sql, $params, $pdo_fetch_mode);
		
		while($row = $query->fetch())
		{
			$tamp[]= array( 
				'gl_key' => $row->glkey,
				'gl_description' => $row->gldescr
			);
		}
		*/
	}
	
	function select($sql, $params = array(), $pdo_fetch_mode=PDO::FETCH_ASSOC)
	{
		$tmd = NULL;
		$conn = $this->db_connect();
		
		try
		{
			$tmd = $conn->prepare($sql);
			$tmd->setFetchMode($pdo_fetch_mode);
			$tmd->execute($params);
		}
		catch(PDOException $e)
		{
			$output = array("Status" => "ERROR","Message" => $e->getMessage());
			file_put_contents('PDOErrors.txt', 
				time() .': '. $e->getMessage(), FILE_APPEND);
		}
		
		return $tmd->fetchAll($pdo_fetch_mode);
	}
	
	public function execute($sql='', $params = array())
	{
		if(sizeof($params) === 0)
		{
			return array("Status" => "ERROR","Message" => 'No parameters found. insert process aborted.');
		}
		
		$tmd = NULL;
		$conn = NULL;
		
		$conn = $this->db_connect();
		
		try
		{
			$tmd = $conn->prepare($sql);
			$tmd->execute($params);
			
			$output = array("Status" => "OK","record_changes" => $params);
			
		}
		catch(PDOException $e)
		{
			$output = array("Status" => "ERROR","Message" => $e->getMessage());
			file_put_contents('PDOErrors.txt', 
				time() .': '. $e->getMessage(), FILE_APPEND);
			
		}
		
		return $output;
	}
}
